import os
import uuid
import hashlib
import logging
import tempfile
import concurrent.futures
import json


from django.utils import timezone
from django.db import close_old_connections
from moviepy import *

from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest


from django.conf import settings

from VideoHandler.models import MaterialVideoData, MaterialCategoryMerge, TaskInfo, TaskSubInfo
from VideoHandler.utils import oss_utils, feishu_utils
from VideoHandler.utils.integrade_video import integrate_video, download_file, delete_file

logger = logging.getLogger('VideoHandler')

pool = concurrent.futures.ThreadPoolExecutor(max_workers=1, thread_name_prefix='integrate_video')

def resolve_integrate(task_req):
    close_old_connections()

    # 根据taskId查询主任务
    task_id = task_req['taskId']
    user_id = task_req['userId']
    sub_task_id = None
    if 'subTaskId' in task_req:
        sub_task_id = task_req['subTaskId']
    video_info_list = task_req['videoTemplateList']

    task_info = TaskInfo.objects.get(id=task_id)

    # 发送飞书  任务开始
    feishu_utils.send_to_person_in_group(user_id, f"[视频融合] --> 任务{task_info.task_name}开始.", f"预计生成视频数量[{len(video_info_list)}]")

    completed_count = 0
    futures = []
    for video_info in video_info_list:
        future = pool.submit(resolve_integrate_item, video_info, user_id, task_id, sub_task_id)
        futures.append(future)
    for future in concurrent.futures.as_completed(futures):
        try:
            result = future.result()
            if result:
                completed_count += 1
        except Exception as e:
            logger.error(f"任务执行出错: {e}", exc_info=True)
    if sub_task_id:
        # 更新子任务
        if completed_count == len(video_info_list):
            # 成功
            TaskSubInfo.objects.filter(id=sub_task_id).update(status=1, update_time=timezone.now())
        else:
            # 失败
            resp_body = {
                '完成数量/总数量': str(completed_count) + '/' + str(len(video_info_list))
            }
            TaskSubInfo.objects.filter(id=sub_task_id).update(status=0, response_body=resp_body, update_time=timezone.now())

    close_old_connections()
    # 更新主任务信息
    TaskInfo.objects.filter(id=task_id).update(status=1, update_time=timezone.now())

    # 发送飞书  任务结束
    feishu_utils.send_to_person_in_group(user_id, f"[视频融合] --> 任务{task_info.task_name}完成.", f"生成视频数量[{completed_count}]")


def resolve_integrate_item(video_info, user_id, task_id, sub_task_id):
    result = True
    # 创建子任务
    close_old_connections()
    task_sub_info = TaskSubInfo(task_id=task_id, status=1, create_time=timezone.now(), update_time=timezone.now())

    filename = str(uuid.uuid4()).replace('-', '') + '.mp4'
    temp_result_path = settings.TEMP_PATH + filename
    integrate_result = False

    # 捕获视频合成时异常
    try:
        integrate_result = integrate_video(video_info, temp_result_path)
    except Exception as e:
        logger.error(f"taskId[{task_id}] 执行失败: {video_info}", exc_info=e)
        task_sub_info.response_body = json.dumps({
            'error': str(e),
            'video_info': video_info
        })
        task_sub_info.status = 0
        result = False
    except SystemExit as se:
        logger.error(f"taskId[{task_id}] 执行异常退出: {video_info}", exc_info=se)
        task_sub_info.response_body = json.dumps({
            'error': str(se),
            'video_info': video_info
        })
        task_sub_info.status = 0
        result = False

    if integrate_result:
        # 获取文件md5
        file_md5 = get_file_md5(temp_result_path)

        # 上传oss
        manifest = video_info['manifest']
        app_id = manifest['appId']
        width = manifest['width']
        height = manifest['height']
        mix_type = manifest['mixType'] if 'mixType' in manifest else 0

        file_path_info = oss_utils.upload_to_oss(temp_result_path, manifest['bucketName'], get_oss_directory(9))

        file_path = file_path_info[0]
        filename = file_path_info[1]

        # 获取视频信息（时长、封面）
        video_clip = VideoFileClip(temp_result_path)
        duration = video_clip.duration

        cover_file_name = str(uuid.uuid4()).replace('-', '') + '.png'
        temp_cover_path = settings.TEMP_PATH + cover_file_name
        cover_index = 1
        if 'coverIndex' in manifest:
            cover_index = manifest['coverIndex']
        video_clip.save_frame(temp_cover_path, t=cover_index)
        video_clip.close()

        # 价格信息
        price_info = None
        if 'priceInfo' in video_info:
            price_info = video_info['priceInfo']

        # 上传视频封面
        oss_cover_path = oss_utils.upload_to_oss(temp_cover_path, manifest['bucketName'], get_oss_directory(8))[0]

        close_old_connections()
        # 存入合成视频信息
        material_video_data = MaterialVideoData(material_name=filename, url=file_path, type=8, user_id=user_id,
                                                cover=oss_cover_path, width=width, height=height, duration=duration,
                                                create_time=timezone.now(), update_time=timezone.now(), source_type=0,
                                                app_id=app_id,
                                                mix_type=mix_type, price_info=price_info, file_md5=file_md5)
        material_video_data.save()
        video_id = material_video_data.id
        # 标签入库
        category_ids = video_info["categoryIds"]
        for category_id in category_ids:
            MaterialCategoryMerge.objects.create(material_type=1, material_data_id=video_id, category_id=category_id,
                                                 create_user=user_id, create_time=timezone.now(),
                                                 update_time=timezone.now())

        # 删除 temp_result_path  temp_cover_path
        os.remove(temp_result_path)
        os.remove(temp_cover_path)

    # 插入子任务信息
    if sub_task_id is None:
        task_sub_info.save()
    return result

def get_file_md5(file_path):
    hash_md5 = hashlib.md5()
    try:
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()
    except FileNotFoundError as fe:
        logger.exception(f"错误：文件 {file_path} 未找到。", exc_info=fe)
        raise fe
    except Exception as e:
        logger.exception(f"错误：发生未知错误。", exc_info=e)
        raise e


def subclip_video(task_req):
    logger.info("task_req: {}".format(task_req))
    url = task_req['url']
    app_id = task_req['appId']
    user_id = task_req['userId']
    task_id = task_req['taskId']
    category_ids = task_req["categoryIds"]

    # 下载视频到本地
    local_url = download_file(url, '.mp4')
    duration_list = task_req['durationList']
    # clip_list = []
    for duration_item in duration_list:
        sub_status = 2
        if 'detext' in task_req and task_req['detext']:
            sub_status = 4
        task_sub_info = TaskSubInfo(task_id=task_id, status=sub_status, create_time=timezone.now(), update_time=timezone.now())
        try:
            video_clip = VideoFileClip(local_url)
            start_second = duration_item[0]
            end_second = duration_item[1]
            if end_second - start_second > 180:
                task_sub_info.response_body = '视频时长超过限制（180s）'
                task_sub_info.status = 0
            else:
                clip_item = video_clip.subclipped(start_second, end_second)
                # 視頻写入本地临时文件
                temp_clip_url = tempfile.gettempdir() + "/" + str(uuid.uuid4()) + '.mp4'
                clip_item.write_videofile(temp_clip_url, codec="libx264", fps=30, threads=2)
                clip_item.close()

                file_path_info = oss_utils.upload_to_oss(temp_clip_url, 'crowdpack', get_oss_directory(6))

                file_path = file_path_info[0]
                filename = file_path_info[1]
                body = {
                    'url': file_path,
                    'materialName': filename,
                    'userId': user_id,
                    'appId': app_id,
                    'categoryIds': category_ids
                }
                task_sub_info.body = json.dumps(body)

                close_old_connections()

                video_clip.close()
                # 刪除封面 視頻
                # os.remove(temp_cover_path)
                os.remove(temp_clip_url)
        except FileNotFoundError as fe:
            logger.error(f"taskId[{task_id}] 执行失败", exc_info=fe)
            task_sub_info.response_body = str(fe)
            task_sub_info.status = 0
        except SystemExit as se:
            logger.error(f"taskId[{task_id}] 执行异常退出", exc_info=se)
            task_sub_info.response_body = str(se)
            task_sub_info.status = 0
        task_sub_info.save()
    os.remove(local_url)

    # 等待java服务进行字幕擦除、审核等后续流程
    TaskInfo.objects.filter(id=task_id).update(status=2, update_time=timezone.now())

def audio_extract(url, audio_path):
    video_path = 'https://crowdpack.oss-cn-beijing.aliyuncs.com' + url
    local_url = download_file(video_path, '.mp4')
    video_clip = VideoFileClip(local_url)
    if video_clip.audio is not None:
        video_clip.audio.write_audiofile(
            audio_path,
            codec="pcm_s16le",
            fps=16000  # 采样率（16000Hz，可根据需求修改）
        )
    video_clip.close()
    delete_file(local_url)

def create_extract_task(url):
    client = AcsClient(settings.OSS_CONFIG['access_key_id'], settings.OSS_CONFIG['access_key_secret'], 'cn-shanghai')
    # 提交录音文件识别请求
    post_request = CommonRequest()
    post_request.set_domain('filetrans.cn-shanghai.aliyuncs.com')
    post_request.set_version('2018-08-17')
    post_request.set_product('nls-filetrans')
    post_request.set_action_name('SubmitTask')
    post_request.set_method('POST')
    # 新接入请使用4.0版本，已接入（默认2.0）如需维持现状，请注释掉该参数设置。
    # 设置是否输出词信息，默认为false，开启时需要设置version为4.0。
    task = {'appkey': settings.OSS_CONFIG['audio_recognition_app_key'], 'file_link': url, 'version': "4.0", 'enable_words': False}
    # 开启智能分轨，如果开启智能分轨，task中设置KEY_AUTO_SPLIT为True。
    task = json.dumps(task)
    print(task)
    post_request.add_body_params('Task', task)
    job_id = None
    try:
        post_response = client.do_action_with_exception(post_request)
        post_response = json.loads(post_response)
        print(post_response)
        status_text = post_response['StatusText']
        if status_text == 'SUCCESS':
            print("录音文件识别请求成功响应！")
            job_id = post_response['TaskId']
        else:
            print("录音文件识别请求失败！")
            return
    except ServerException as e:
        print(e)
    except ClientException as e:
        print(e)
    return job_id

def extract_text(task_req):
    url = task_req['url']
    sub_task_id = task_req['subTaskId']
    response_body = None
    status = 0

    job_id = None

    # 1. 提取视频中的音频，转为标准PCM格式
    filename = str(uuid.uuid4()).replace('-', '') + '.wav'
    local_audio_path = settings.TEMP_PATH + filename
    # 2. 提交文案提取任务，获取jobId 存入子任务信息中
    audio_extract(url, local_audio_path)
    if os.path.isfile(local_audio_path):
        # 上传oss
        remote_audio_path_info = oss_utils.upload_to_oss(local_audio_path, 'crowdpack', get_oss_directory(10))
        remote_audio_path = 'https://crowdpack.oss-cn-beijing.aliyuncs.com' + remote_audio_path_info[0]
        # 创建文案提取任务
        job_id = create_extract_task(remote_audio_path)
        if job_id is None:
            response_body = {
                'message': '音频识别任务提交失败'
            }
        else:
            # 任务创建成功
            status = 10
        delete_file(local_audio_path)
    else:
        # 音频提取失败
        response_body = {
            'message': '音频提取执行失败'
        }
    if job_id is None:
        TaskSubInfo.objects.filter(id=sub_task_id).update(status=status, response_body = json.dumps(response_body), update_time=timezone.now())
    else:
        TaskSubInfo.objects.filter(id=sub_task_id).update(status=status, querys=job_id, response_body = json.dumps(response_body), update_time=timezone.now())





def get_oss_directory(file_type):
    if file_type == 0:
        return "material-img/template"
    elif file_type == 1:
        return "material-img/origin"
    elif file_type == 2:
        return "material-img/final"
    elif file_type == 3:
        return "material-video/logo"
    elif file_type == 4:
        return "material-video/prompt"
    elif file_type == 5:
        return "material-video/prefix"
    elif file_type == 6:
        return "material-video/body"
    elif file_type == 7:
        return "material-video/suffix"
    elif file_type == 8:
        return "material-video/cover"
    elif file_type == 9:
        return "material-video/final"
    elif file_type == 10:
        return "material-img/tmp"
    elif file_type == 11:
        return "material-audio"
    else:
        return "material-audit"